home *** CD-ROM | disk | FTP | other *** search
/ C# & Game Programming - A…er's Guide (2nd Edition) / Buono 2nd Ed.iso / Chapter3 / AsteroidMiner / AsteroidMiner DirectX9 / AsteroidMinerDX.cs < prev    next >
Text File  |  2004-09-02  |  32KB  |  777 lines

  1. /* AsteroidMiner v2.1 DirectX Edition - by Salvatore A. Buono */
  2. using System;
  3. using System.Drawing;
  4. using System.Windows.Forms;
  5. using System.IO; // for file read/write
  6. using Microsoft.DirectX;
  7. using Microsoft.DirectX.DirectDraw;
  8. using Microsoft.DirectX.DirectSound;
  9. using Buffer = Microsoft.DirectX.DirectSound.SecondaryBuffer;
  10. using Microsoft.DirectX.DirectInput;
  11.  
  12. using GameClasses; // Our common utility class
  13.  
  14. namespace Games {
  15.     public class AsteroidMiner : System.Windows.Forms.Form {
  16.         // DirectDraw
  17.         private Microsoft.DirectX.DirectDraw.Device draw = null;
  18.         private Surface primary = null; 
  19.         private Surface buffer = null;
  20.         private Surface[] PlayerLDraw = new Surface[9];
  21.         private Surface[] PlayerRDraw = new Surface[9];
  22.         private Surface[] ExhaustDraw = new Surface[9];
  23.         private Rectangle destination = new Rectangle();  
  24.  
  25.         // DirectSound
  26.         private Microsoft.DirectX.DirectSound.Device sound = null;
  27.         private SecondaryBuffer SoundBuffer = null;
  28.  
  29.         // DirectInput
  30.         private Microsoft.DirectX.DirectInput.Device keyboard = null;
  31.         private Microsoft.DirectX.DirectInput.Device joystick = null;
  32.         public JoystickState State = new JoystickState();
  33.         private System.Windows.Forms.Timer Joystick; 
  34.         private System.Windows.Forms.Timer Keyboard; 
  35.  
  36.         // Constants
  37.         const int SCALE = 25;
  38.         const string MEDIA_ROOT = @".\media\";
  39.         const int TIMER_BASE = 40;    // in millis.
  40.         const int MAX_STARS = 20;     // # of stars
  41.         const int MAX_ASTEROIDS = 4;  // # of asteroids (if on)
  42.         const int MAX_MISSILES = 4;   // # of missiles/ship
  43.         const int MISSILE_LIFE = 50;  // 40 ticks
  44.         const int THRUSTER_LIFE = 10; // 10 ticks
  45.         const int ASTEROID_SPEED = 10;// how much they move
  46.         const int GRAVITY_SPEED = 75; // how often gravity affects you
  47.         const int GRAVITY_EFFECT = 5; // how much it effects you
  48.         const int DEFAULT_SPEED = 5;  // default game speed
  49.         const int NUM_LIVES = 5;      // determines when the game is over
  50.         const int SCORE_ASTEROID = 10; // 10 points for hitting an asteroid
  51.  
  52.  
  53.         // Member fields -- UI elements
  54.         private System.Windows.Forms.MainMenu mainMenu1;
  55.         private System.Windows.Forms.MenuItem menuItem1;
  56.         private System.ComponentModel.Container components = null;
  57.  
  58.         // Member fields
  59.         GameState gameState;  // Holds state information
  60.         PlayerImageArray player;
  61.  
  62.         AnimatedImage[] exhaustImages;
  63.         private Surface[] PlayerMissilesDraw = new Surface[MAX_MISSILES];
  64.         AnimatedImage[] playerMissiles;
  65.         private Surface[] AsteroidDraw = new Surface[MAX_ASTEROIDS];
  66.         AnimatedImage[] asteroids;
  67.  
  68.         Point[] stars;  // background star field
  69.         private Surface[] StarsDraw = new Surface[MAX_STARS];
  70.  
  71.         GameTimer timer;    // Event timer to control all animation
  72.         TimedEvent p1Event; // The player event, for easy reference
  73.  
  74.         int HighScore = 0;        // Game's top score loads from file
  75.         int p1LastDirection = 0;
  76.  
  77.         // Main entry-point function
  78.         [STAThread] static void Main() {
  79.             Application.Run(new AsteroidMiner());
  80.         }
  81.  
  82.     #region Set-up & Initialization
  83.         // Default constructor
  84.         public AsteroidMiner() {
  85.             InitializeComponent();
  86.  
  87.             // DirectDraw
  88.             draw = new Microsoft.DirectX.DirectDraw.Device();
  89.             draw.SetCooperativeLevel(this, 
  90.                 Microsoft.DirectX.DirectDraw.CooperativeLevelFlags.FullscreenExclusiveAllowModex); 
  91.             draw.SetDisplayMode(640, 480, 32, 0, false); 
  92.             CreateSurfaces();
  93.  
  94.             // DirectSound
  95.             sound = new Microsoft.DirectX.DirectSound.Device();
  96.             sound.SetCooperativeLevel(this, CooperativeLevel.Priority);
  97.       
  98.  
  99.             gameState = new GameState();
  100.  
  101.             // Set up timer & timed events
  102.             timer = new GameTimer(TIMER_BASE);
  103.             TimedEvent newEvent;
  104.  
  105.             // Initialize the two player objects
  106.             player = new PlayerImageArray(9);
  107.             player.constraintBox = this.ClientRectangle;
  108.  
  109.             // player thruster event
  110.             newEvent = new TimedEvent(THRUSTER_LIFE, 
  111.                 new TickHandler(PlayerThrust));
  112.             newEvent.payload = player;
  113.             timer.addEvent(newEvent, 1, "player");
  114.             p1Event = newEvent;
  115.  
  116.             // Initialize exhaust images
  117.             exhaustImages = new AnimatedImage[9];
  118.             for (int i = 0; i < 9; i ++) {
  119.                 exhaustImages[i] = new AnimatedImage(0, 0);
  120.                 exhaustImages[i].RescaleImage(SCALE / 2, SCALE / 2);
  121.             }
  122.             // Set correct image offsets
  123.             exhaustImages[1].imageOffsetX = 14;
  124.             exhaustImages[1].imageOffsetY = -5;
  125.             exhaustImages[2].imageOffsetX = 9;
  126.             exhaustImages[2].imageOffsetY = -9;
  127.             exhaustImages[3].imageOffsetX = 0;
  128.             exhaustImages[3].imageOffsetY = -6;
  129.             exhaustImages[4].imageOffsetX = -8;
  130.             exhaustImages[4].imageOffsetY = 6;
  131.             exhaustImages[5].imageOffsetX = 1;
  132.             exhaustImages[5].imageOffsetY = 17;
  133.             exhaustImages[6].imageOffsetX = 8;
  134.             exhaustImages[6].imageOffsetY = 20;
  135.             exhaustImages[7].imageOffsetX = 11;
  136.             exhaustImages[7].imageOffsetY = 17;
  137.             exhaustImages[8].imageOffsetX = 20;
  138.             exhaustImages[8].imageOffsetY = 6;
  139.  
  140.             // Initialize the background stars
  141.             Random rnd = new Random();
  142.             stars = new Point[MAX_STARS];
  143.             for (int i = 0; i < MAX_STARS; i++)
  144.                 stars[i] = new Point(rnd.Next(ClientSize.Width), rnd.Next(ClientSize.Height));
  145.  
  146.             // Initialize player's missiles
  147.             playerMissiles = new AnimatedImage[MAX_MISSILES];
  148.             for (int i = 0; i < MAX_MISSILES; i++) {
  149.                 playerMissiles[i] = new AnimatedImage(0, 0);
  150.                 playerMissiles[i].owner = player;
  151.                 playerMissiles[i].constraintBox = this.ClientRectangle;
  152.                 playerMissiles[i].RescaleImage(SCALE / 5, SCALE / 5);
  153.  
  154.                 // Set up timer & events
  155.                 newEvent = new TimedEvent(MISSILE_LIFE, 
  156.                     new TickHandler(MoveMissile));
  157.                 newEvent.payload = playerMissiles[i];
  158.                 timer.addEvent(newEvent, 1, "playerMissile" + i);
  159.             }
  160.  
  161.             // Initialize the asteroids
  162.             asteroids = new AnimatedImage[MAX_ASTEROIDS];
  163.             for (int i = 0; i < MAX_ASTEROIDS; i++) {
  164.                 // Note, there are only 4 image positions so strange
  165.                 // things happen when MAX_ASTEROIDS > 4
  166.                 asteroids[i] = new AnimatedImage((i % 2 == 1) ? 3 * SCALE :
  167.                     20 * SCALE, (i < 2 ? 3 * SCALE : 12 * SCALE));
  168.                 asteroids[i].direction = rnd.Next(1, 8);
  169.                 asteroids[i].constraintBox = this.ClientRectangle;
  170.                 asteroids[i].RescaleImage(2 * SCALE, 2 * SCALE);
  171.  
  172.                 newEvent = new TimedEvent(new TickHandler(MoveAsteroids));
  173.                 newEvent.payload = asteroids[i];
  174.                 timer.addEvent(newEvent, ASTEROID_SPEED, "asteroid" + i);
  175.                 newEvent.isActive = true;
  176.             }
  177.  
  178.             newEvent = new TimedEvent(new TickHandler(Gravity));
  179.             newEvent.isActive = true;
  180.             timer.addEvent(newEvent, GRAVITY_SPEED, "");
  181.  
  182.             // Call Invalidate() every tick
  183.             newEvent = new TimedEvent(new TickHandler(ScreenRefresh));
  184.             timer.addEvent(newEvent, 1, "invalidate");
  185.             newEvent.isActive = true;
  186.  
  187.  
  188.       
  189.         }
  190.  
  191.             #region DirectDraw CreateSurfaces
  192.         private void CreateSurfaces() {
  193.             SurfaceDescription description = new SurfaceDescription(); 
  194.             description.SurfaceCaps.PrimarySurface = true; 
  195.             description.SurfaceCaps.Flip = true;
  196.             description.SurfaceCaps.Complex = true;        
  197.             description.BackBufferCount = 1; 
  198.  
  199.             primary = new Surface(description, draw); 
  200.  
  201.             SurfaceCaps caps = new SurfaceCaps();
  202.             caps.BackBuffer = true; 
  203.             buffer = primary.GetAttachedSurface(caps);      
  204.  
  205.  
  206.             description.Clear();
  207.  
  208.             try {
  209.                 for(int i = 1; i < 9; i++) {                    
  210.                     PlayerLDraw[i] = new Surface(MEDIA_ROOT + "Ship1-" + i + ".bmp", description, draw);
  211.  
  212.                     PlayerRDraw[i] = new Surface(MEDIA_ROOT + "Ship2-" + i + ".bmp", description, draw);
  213.  
  214.                     ExhaustDraw[i] = new Surface(MEDIA_ROOT + "Exhaust" + i + ".bmp", description, draw);
  215.                 }
  216.                 
  217.                 for(int i = 0; i < MAX_MISSILES; i++) {
  218.                     PlayerMissilesDraw[i] = new Surface(MEDIA_ROOT + "Weapon1.bmp", description, draw);
  219.                 }
  220.                     
  221.                 for(int i = 0; i < MAX_STARS; i++) {
  222.      StarsDraw[i] = new Surface(MEDIA_ROOT + "FireBall.bmp", description, draw);} 
  223.             
  224.                 PlayerLDraw[0] = new Surface(MEDIA_ROOT + "FireBall.bmp", description, draw);
  225.                 PlayerRDraw[0] = new Surface(MEDIA_ROOT + "FireBall.bmp", description, draw); 
  226.  
  227.                 for(int i = 0; i < MAX_ASTEROIDS; i++) {
  228.                      AsteroidDraw[i] = new Surface(MEDIA_ROOT + "Asteroid" + i + ".bmp", description, draw);}
  229.             }
  230.             catch (FileNotFoundException ex) {
  231.                 MessageBox.Show("Please check that the following file exists:\n\n" + 
  232.                     ex.Message + "\n\nCorrect the file referenced in the config file.", 
  233.                     "File Not Found!");
  234.                 Application.Exit();}
  235.             catch{Application.Exit();}
  236.         }
  237.  
  238. #endregion
  239.  
  240.         // (re) Sets positional data, scores, and other game data
  241.         public void Setup() {
  242.             gameState.currentState = GameState.State.Started;
  243.             gameState.currentSpeed = DEFAULT_SPEED;
  244.  
  245.             player.imagePosX = 12 * SCALE - (player.imageWidth >> 1);
  246.             player.imagePosY = 8 * SCALE;
  247.  
  248.             if (player.score > HighScore)
  249.                 SaveHighScore(player.score);
  250.             HighScore = LoadHighScore();
  251.             if(HighScore < player.score) {
  252.         HighScore = player.score;}
  253.             player.score = 0;
  254.  
  255.       
  256.             player.deaths = 0;
  257.             player.isActive = true;
  258.             player.direction = AnimatedImage.NORTH;
  259.  
  260.             for (int i = 0; i < MAX_MISSILES; i++) {
  261.                 playerMissiles[i].isActive = false;
  262.             }
  263.  
  264.             for (int i = 0; i < MAX_ASTEROIDS; i++) {
  265.                 asteroids[i].RescaleImage(2 * SCALE, 2 * SCALE);
  266.                 asteroids[i].isActive = true;
  267.             }
  268.  
  269.             timer.Start();
  270.         }
  271.  
  272.         // Form's initialize method - we initialize all form related items
  273.         private void InitializeComponent() {
  274.             this.components = new System.ComponentModel.Container();
  275.             this.mainMenu1 = new System.Windows.Forms.MainMenu();
  276.             this.menuItem1 = new System.Windows.Forms.MenuItem();
  277.             // 
  278.             // mainMenu1
  279.             // 
  280.             this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
  281.                                                                                       this.menuItem1});
  282.             // 
  283.             // menuItem1
  284.             // 
  285.             this.menuItem1.Index = 0;
  286.             this.menuItem1.Text = "Asteroid Miner";
  287.             this.menuItem1.Click += new System.EventHandler(this.SinglePlayer_Click);
  288.             // 
  289.             // AsteroidMiner
  290.             // 
  291.             this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
  292.             this.BackColor = System.Drawing.Color.Black;
  293.             this.ClientSize = new System.Drawing.Size(600, 400);
  294.             this.Menu = this.mainMenu1;
  295.             this.Name = "AsteroidMiner";
  296.             this.Text = "Asteroid Miner";
  297.             this.Load += new System.EventHandler(this.AsteroidMiner_Load);
  298.  
  299.             this.Joystick = new System.Windows.Forms.Timer(this.components);
  300.             this.Joystick.Tick += new System.EventHandler(this.JoyStick_Tick);
  301.  
  302.             this.Keyboard = new System.Windows.Forms.Timer(this.components);
  303.             this.Keyboard.Tick += new System.EventHandler(this.Keyboard_Tick);
  304.             
  305.         }
  306.     #endregion
  307.  
  308.     #region Game-play functions
  309.         // Start up the player's thrusters
  310.         public void EngageThrusters(Player player) {
  311.             if (player.isActive) {
  312.                 if (player == player)
  313.                     p1Event.Reset(); // reset the timer from now
  314.                 try {
  315.                     SoundBuffer = new SecondaryBuffer(MEDIA_ROOT + "Thrust.wav", sound);
  316.                     SoundBuffer.Play(0, BufferPlayFlags.Default);}
  317.                 catch{Utils.PlaySound(MEDIA_ROOT + "Thrust.WAV");} 
  318.             }
  319.  
  320.             return;
  321.         }
  322.  
  323.         // Fire a missile (if any available) from the given player
  324.         public void FireMissile(Player player, AnimatedImage[] missiles) {
  325.             if (!player.isActive)
  326.                 return;
  327.  
  328.             for (int i = 0; i < MAX_MISSILES; i++) {
  329.                 // shoot the first missile that's not already out there!
  330.                 if (!missiles[i].isActive) {
  331.                     // set missiles properties so that it will get 
  332.                     // properly animated
  333.                     missiles[i].isActive = true;
  334.                     missiles[i].direction = player.direction;
  335.                     missiles[i].imagePosX = player.imagePosX;
  336.                     missiles[i].imagePosY = player.imagePosY;
  337.  
  338.                     TimedEvent missileEvent = timer.getEvent
  339.                         ("playerMissile" + i);
  340.                     missileEvent.Reset();
  341.  
  342.                     try {
  343.                         SoundBuffer = new SecondaryBuffer(MEDIA_ROOT + "Fire.wav", sound);
  344.                         SoundBuffer.Play(0, BufferPlayFlags.Default);}
  345.                     catch{Utils.PlaySound(MEDIA_ROOT + "Fire.WAV");}    
  346.                     break;
  347.                 }
  348.             }
  349.         }
  350.  
  351.         public void ExertGravitationalPull(Player player) {
  352.             if (player.imagePosX != ClientSize.Width >> 1)
  353.                 player.imagePosX += (player.imagePosX > 
  354.                     (ClientSize.Width >> 1)) ? -GRAVITY_EFFECT : GRAVITY_EFFECT;
  355.             if (player.imagePosY != ClientSize.Height >> 1)
  356.                 player.imagePosY += (player.imagePosY > 
  357.                     (ClientSize.Height >> 1)) ? -GRAVITY_EFFECT : GRAVITY_EFFECT;
  358.         }
  359.     #endregion
  360.  
  361.     #region Collision Detection Routines
  362.         // Ships can run into the asteroids and missiles.
  363.         // We'll assume that the missiles will detect their collisions
  364.         // so we only have to worry about the others.
  365.         public bool CheckShipCollision(Player player) {      
  366.             Player opponent = player;
  367.  
  368.             // Asteroid collision?
  369.             for (int i = 0; i < MAX_ASTEROIDS; i++) {
  370.                 if (asteroids[i].isActive) {
  371.                     if (asteroids[i].Intersects(player)) {
  372.                         ShipCollided(player);
  373.                         return true;
  374.                     }
  375.                 }
  376.             }
  377.  
  378.             return false;
  379.         }
  380.  
  381.         // Asteroids can run into ships & missiles. We'll assume that 
  382.         // missiles will detect their collisions themselves since they 
  383.         // update frequently.
  384.         public bool CheckAsteroidCollision() {
  385.             for (int i = 0; i < MAX_ASTEROIDS; i++) {
  386.                 if (asteroids[i].isActive) {
  387.                     if (asteroids[i].Intersects(player)) {
  388.                         ShipCollided(player);
  389.                         return true;
  390.                     }
  391.                 }
  392.             }
  393.  
  394.             return false;
  395.         }
  396.  
  397.         // Missiles can only run into asteroids.
  398.         public bool CheckMissileCollision(AnimatedImage missile) {
  399.             if (missile.isActive) {
  400.                 for (int i = 0; i < MAX_ASTEROIDS; i++) {
  401.                     if (missile.Intersects(asteroids[i])) {
  402.                         AsteroidCollided(asteroids[i]);
  403.                         missile.isActive = false;
  404.                         try {
  405.                             SoundBuffer = new SecondaryBuffer(MEDIA_ROOT + "Hit.wav", sound);
  406.                             SoundBuffer.Play(0, BufferPlayFlags.Default);}
  407.                         catch{Utils.PlaySound(MEDIA_ROOT + "Hit.WAV");}   
  408.                         return true;
  409.                     }
  410.                 }
  411.             }
  412.  
  413.             return false;  // Must not have hit anything!
  414.         }
  415.  
  416.         // The ship ran into something! Add to the death count and
  417.         // end the game if we're out of lives.
  418.         public void ShipCollided(Player victim) {
  419.             if (!victim.isActive)
  420.                 return; // He's already dead!
  421.  
  422.             victim.isActive = false;
  423.             victim.direction = 0; // index of our explosion image
  424.             try            {
  425.                 SoundBuffer = new SecondaryBuffer(MEDIA_ROOT + "Hit.wav", sound);
  426.                 SoundBuffer.Play(0, BufferPlayFlags.Default);}
  427.             catch{Utils.PlaySound(MEDIA_ROOT + "Hit.WAV");}      
  428.  
  429.             victim.deaths++;
  430.  
  431.             if(victim.deaths >= NUM_LIVES) {
  432.                 gameState.currentState = GameState.State.Stopped;
  433.  
  434.                 if (victim.score > HighScore) {
  435.                     SaveHighScore(victim.score);
  436.                 }
  437.                 Application.Exit();
  438.             }
  439.         }
  440.  
  441.         // The asteroid was hit! Cycle through its different scaled
  442.         // display & increment the player's score.
  443.         public void AsteroidCollided(AnimatedImage victim) {
  444.             int scaleFactor = (victim.imageWidth <= (SCALE / 2)) ? 
  445.                 SCALE * 2 : victim.imageWidth / 2;
  446.             victim.RescaleImage(scaleFactor, scaleFactor);
  447.  
  448.             player.score += SCORE_ASTEROID;
  449.         }
  450.     #endregion
  451.  
  452.     #region OnPaint
  453.         protected override void OnPaint(PaintEventArgs e) {
  454.             // Write start-up text if game is stopped
  455.             if (gameState.currentState == GameState.State.Stopped) {
  456.                 Brush YellowBrush = Brushes.Yellow;
  457.                 Font LargeAlgerianFont = new Font("Algerian", 24);
  458.                 Font MidAlgerianFont = new Font("Algerian", 18);
  459.                 Graphics g = e.Graphics; // Screen's graphics object
  460.                 g.DrawString("C# and Game Programming", LargeAlgerianFont, YellowBrush, 2 * SCALE, SCALE);
  461.                 g.DrawString("A Beginner's Guide", LargeAlgerianFont, YellowBrush, 9 * SCALE, 5 * SCALE);
  462.                 g.DrawString("By Salvatore A. Buono", MidAlgerianFont, YellowBrush, 2 * SCALE, 14 * SCALE);
  463.             }    else {
  464.                 buffer.ColorFill(Color.Black);
  465.  
  466.                 // Draw the scores
  467.                 buffer.ForeColor = Color.Blue;
  468.                 if(player.score < HighScore) {
  469.                                  buffer.DrawText(4 * SCALE, 2*SCALE, HighScore.ToString(), false);}
  470.                 else { 
  471.                                  buffer.DrawText(4 * SCALE, 2*SCALE, player.score.ToString(), false);}
  472.        
  473.                 buffer.DrawText(16 * SCALE, 2*SCALE, player.score.ToString(), false);
  474.  
  475.                 // Draw the stars
  476.                 for(int i = 0; i < MAX_STARS; i++) {
  477.                              try {
  478.                                 destination = new Rectangle(stars[i].X, stars[i].Y, 10, 10);
  479.                                 buffer.Draw(destination, StarsDraw[i], DrawFlags.Wait);}
  480.                             catch(SurfaceLostException){CreateSurfaces();}
  481.                             catch{}
  482.                             }
  483.  
  484.                 // Draw player 1 & its appropriate exhaust
  485.                 try {
  486.                     destination = new Rectangle(player.imagePosX, player.imagePosY, SCALE, SCALE);
  487.                     buffer.Draw(destination, PlayerLDraw[player.direction], DrawFlags.Wait);}
  488.                 catch(SurfaceLostException){CreateSurfaces();}
  489.                 catch{}
  490.  
  491.                 if (p1Event.isActive) {
  492.                     try {
  493.                         int X = player.imagePosX + exhaustImages[player.direction].imageOffsetX;
  494.                         int Y = player.imagePosY + exhaustImages[player.direction].imageOffsetY;
  495.                         destination = new Rectangle(X, Y, SCALE/2, SCALE/2);
  496.                         buffer.Draw(destination, ExhaustDraw[player.direction], DrawFlags.Wait);}
  497.                     catch(SurfaceLostException){CreateSurfaces();}
  498.                     catch{}
  499.                 }
  500.  
  501.                 // Draw missiles
  502.                 for (int i = 0; i < MAX_MISSILES; i ++) {
  503.                     if (playerMissiles[i].isActive)
  504.                         try {
  505.                             destination = new Rectangle(playerMissiles[i].imagePosX, 
  506.                                 playerMissiles[i].imagePosY, SCALE/5, SCALE/5);
  507.                             buffer.Draw(destination, PlayerMissilesDraw[i], DrawFlags.Wait);}
  508.                         catch(SurfaceLostException){CreateSurfaces();}
  509.                         catch{}
  510.                                             
  511.                 }
  512.  
  513.                 // Draw asteroids
  514.                 for (int i = 0; i < MAX_ASTEROIDS; i++) {
  515.                     try {
  516.                         destination = new Rectangle(asteroids[i].imagePosX, 
  517.                             asteroids[i].imagePosY, asteroids[i].imageWidth, asteroids[i].imageHeight);
  518.                         buffer.Draw(destination, AsteroidDraw[i], DrawFlags.Wait);}
  519.                     catch(SurfaceLostException){CreateSurfaces();}
  520.                     catch{}
  521.                 }
  522.                 primary.Flip(buffer, FlipFlags.DoNotWait);
  523.             }
  524.             buffer.Dispose();
  525.         }
  526.     #endregion
  527.  
  528.             #region Keyboard_Tick 
  529.         // Capture keyboard input for player control
  530.         private void Keyboard_Tick(object sender, System.EventArgs e) {
  531.              Microsoft.DirectX.DirectXException.IgnoreExceptions ();
  532.             if (gameState.currentState != GameState.State.Started) 
  533.                 return;
  534.  
  535.             keyboard = new Microsoft.DirectX.DirectInput.Device(SystemGuid.Keyboard);
  536.             keyboard.Properties.BufferSize = 8; 
  537.             keyboard.Acquire();
  538.  
  539.             KeyboardState state = keyboard.GetCurrentKeyboardState();
  540.  
  541.             for (Key k = Key.Escape; k <= Key.MediaSelect; k++) { 
  542.                  if (state[k] && (k == Key.Space || k == Key.NumPad0)) {
  543.                      FireMissile(player, playerMissiles);}
  544.                  else if (state[k] && k == Key.Up) {
  545.                      p1LastDirection = player.direction;
  546.                      EngageThrusters(player);}
  547.                  else if (state[k] && k == Key.Left) {
  548.                      if (player.isActive)
  549.                          player.RotateDirection(1);}
  550.                  else if (state[k] && k == Key.Right) {
  551.                      if (player.isActive)
  552.                          player.RotateDirection(-1);}
  553.                  else if (state[k] && k == Key.Down) {
  554.                      if (!player.isActive)
  555.                          player.isActive = true;
  556.                      player.RandomizePosition();}
  557.                  else if (state[k] && k == Key.Escape) {
  558.                      Application.Exit();}
  559.             } 
  560.         }
  561. #endregion
  562.  
  563.             #region Joystick
  564.         private void JoyStick_Tick(object sender, System.EventArgs e) {
  565.              Microsoft.DirectX.DirectXException.IgnoreExceptions ();
  566.             foreach (DeviceInstance instance in Manager.GetDevices(DeviceClass.GameControl, 
  567.                 EnumDevicesFlags.AttachedOnly)) {
  568.                 joystick = new Microsoft.DirectX.DirectInput.Device(instance.InstanceGuid);
  569.                 break;}
  570.             if(joystick == null) {return;}
  571.  
  572.             joystick.SetDataFormat(DeviceDataFormat.Joystick);
  573.  
  574.             foreach (DeviceObjectInstance d in joystick.Objects) {
  575.                 if ((0 != (d.ObjectId & (int)DeviceObjectTypeFlags.Axis))) {
  576.                     joystick.Properties.SetRange(ParameterHow.ById, 
  577.                         d.ObjectId, new InputRange(-1000, 1000));}
  578.             }
  579.             joystick.SetCooperativeLevel(this, Microsoft.DirectX.DirectInput.CooperativeLevelFlags.Background | Microsoft.DirectX.DirectInput.CooperativeLevelFlags.NonExclusive);
  580.             joystick.Properties.AxisModeAbsolute = true; 
  581.             joystick.Acquire();
  582.             joystick.Poll();
  583.             State = joystick.CurrentJoystickState;
  584.  
  585.             if(-600 > State.Y) {
  586.                 if (!player.isActive)
  587.                     player.isActive = true;
  588.                 player.RandomizePosition();}
  589.  
  590.             if(player.isActive == true) {
  591.                 if(-400 < State.X && -600 > State.Y) {
  592.                      player.RotateDirection(1);}
  593.                 else if(-500 > State.X && -600 > State.Y) {
  594.                      player.RotateDirection(-1);}
  595.                 else if(-400 < State.X && -500 < State.Y) {
  596.                      player.RotateDirection(1);}
  597.                 else if(-500 > State.X && -500 < State.Y) {
  598.                      player.RotateDirection(-1);}
  599.                 else if(-400 < State.X) {
  600.                      player.RotateDirection(-1);}
  601.                 else if(-500 > State.X) {
  602.                      player.RotateDirection(1);}
  603.                 else if(-500 < State.Y) {
  604.                     p1LastDirection = player.direction;
  605.                     EngageThrusters(player);}
  606.  
  607.                 byte[] buttons = State.GetButtons(); {
  608.                     foreach (byte b in buttons) {
  609.                         if (0 != (b & 0x80)) {
  610.                             for(int i = 0; i < MAX_MISSILES; i++) {
  611.                                 if(playerMissiles[i].isActive == false) {
  612.                                     FireMissile(player, playerMissiles);
  613.                                     break;}
  614.                             }
  615.                         }
  616.                     }
  617.                 }
  618.             }
  619.         }
  620. #endregion
  621.  
  622.     #region TimedEvent Tick Handlers
  623.         // Implements the player's thruster push. Called repetitively until
  624.         // the timer counts down to 0.
  625.         public void PlayerThrust(TimedEvent e, Object obj) {
  626.             if (gameState.currentState == GameState.State.Stopped) {
  627.                 e.isActive = false;
  628.                 return;
  629.             }
  630.  
  631.             Player player = (Player) obj;
  632.             int tempDir = player.direction;
  633.  
  634.             player.direction = p1LastDirection;
  635.             player.Animate();
  636.             player.Animate();
  637.             player.direction = tempDir;
  638.             player.WrapInBox();
  639.  
  640.             CheckShipCollision(player);
  641.         }
  642.  
  643.         // Implements the missile's movement. Called automatically until
  644.         // the timer counts down to 0.
  645.         public void MoveMissile(TimedEvent e, Object obj) {
  646.             if (gameState.currentState == GameState.State.Stopped) {
  647.                 e.isActive = false;
  648.                 return;
  649.             }
  650.  
  651.             // Get a proper missile object from the event
  652.             AnimatedImage missile = (AnimatedImage) obj;
  653.             for(int i = 0; i < gameState.currentSpeed; i++) {
  654.                 missile.Animate();
  655.                 missile.WrapInBox();
  656.  
  657.                 // See if we hit anything!
  658.                 CheckMissileCollision(missile);
  659.             }
  660.  
  661.             // disable the missile so it can be fired again
  662.             if (e.tickCounter <= 1)
  663.                 missile.isActive = false;
  664.         }
  665.  
  666.         // Implements the asteroid's random movement. This is called
  667.         // continously throughout the game.
  668.         public void MoveAsteroids(TimedEvent e, Object obj) {
  669.             if (gameState.currentState == GameState.State.Stopped) {
  670.                 e.isActive = false;
  671.                 return;
  672.             }
  673.  
  674.             Random rnd = new Random();
  675.             for(int i = 0; i < MAX_ASTEROIDS; i++) {
  676.                 if (rnd.Next(10) > 7) // Change the direction ~70% of the time
  677.                     asteroids[i].direction = rnd.Next(1, 8);
  678.                 asteroids[i].Animate();
  679.                 asteroids[i].WrapInBox();
  680.                 CheckAsteroidCollision();
  681.             }
  682.         }
  683.  
  684.         // Implements the gravity (from an unseen field -- that only 
  685.         // affects ships -- located at the center of the screen).
  686.         public void Gravity(TimedEvent e, Object obj) {
  687.             if (gameState.currentState == GameState.State.Stopped) {
  688.                 e.isActive = false;
  689.                 return;
  690.             }
  691.  
  692.             if (player.isActive){ // Doesn't pull "dead" ships
  693.                 ExertGravitationalPull(player);
  694.                 CheckShipCollision(player);
  695.             }
  696.  
  697.             try {
  698.                 SoundBuffer = new SecondaryBuffer(MEDIA_ROOT + "Gravity.wav", sound);
  699.                 SoundBuffer.Play(0, BufferPlayFlags.Default);}
  700.             catch{Utils.PlaySound(MEDIA_ROOT + "Gravity.WAV");}    
  701.         }
  702.  
  703.         // Regular refresh function. The screen will redraw every tick.
  704.         public void ScreenRefresh(TimedEvent e, Object obj) {Invalidate();}
  705.     #endregion
  706.  
  707.     #region Menu Click Handler
  708.         private void SinglePlayer_Click(object sender, System.EventArgs e) {
  709.             gameState.currentMode = GameState.Mode.SinglePlayer;
  710.             Setup();
  711.         }
  712.     #endregion
  713.  
  714.     #region File Read/Write
  715.         // Utility function to read the high score from a file
  716.         public int LoadHighScore() {
  717.             StreamReader file;
  718.             string strTemp = "";
  719.             int score = 0;
  720.  
  721.             try {
  722.                 file = new StreamReader(@".\HighScore.txt");
  723.                 for (int i = 0; i < 4; i++)
  724.                     strTemp = file.ReadLine();
  725.                 score = int.Parse(strTemp.Substring(0, strTemp.IndexOf("\t")));
  726.                 file.Close();
  727.             } catch (Exception e) {Console.WriteLine(e);}
  728.  
  729.             return score;
  730.         }
  731.  
  732.         // Utility function to write the high score from a file
  733.         public void SaveHighScore(int score) {
  734.             StreamWriter file;
  735.  
  736.             try {
  737.                 file = new StreamWriter(@".\HighScore.txt");
  738.                 file.WriteLine("Asteroid Miner High Scores");
  739.                 file.WriteLine("--------------------------");
  740.                 file.WriteLine();
  741.                 file.WriteLine(score + "\t" + DateTime.Now);
  742.                 file.Flush();
  743.                 file.Close();
  744.             } catch (Exception e) {Console.WriteLine(e);}
  745.         }
  746.     #endregion
  747.  
  748.         // Clean exit function
  749.         public void ExitApplication() {
  750.             Application.Exit(); 
  751.         }
  752.  
  753.         // The default Invalidate clears the entire screen creating a very 
  754.         // undesireable flicker. This override allows the paint method to
  755.         // control it's redraw.
  756.         public new void Invalidate() {
  757.             OnPaint(new PaintEventArgs(Graphics.FromHwnd(this.Handle), 
  758.                 new Rectangle(new Point(0, 0), this.Size)));
  759.         }
  760.  
  761.         // The system occasionally also calls OnPaintBackground which will
  762.         // mean the screen will be cleared, painted with the background 
  763.         // color, then repainted. This is another cause of flicker, so we
  764.         // just override it and tell it not to do anything!
  765.         protected override void OnPaintBackground(PaintEventArgs e) {
  766.             // Do nothing!
  767.         }
  768.  
  769.         private void AsteroidMiner_Load(object sender, System.EventArgs e) {
  770.              // DirectInput
  771.             Joystick.Start();  
  772.             Keyboard.Start(); 
  773.             
  774.         }
  775.     }
  776. }
  777.